package com.xyy.saas.payment.adpater.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xyy.framework.redis.core.RedisClient;
import com.xyy.saas.payment.adpater.PaymentAdapter;
import com.xyy.saas.payment.adpater.channel.setting.ChannelSettingManager;
import com.xyy.saas.payment.adpater.config.BankConfigManager;
import com.xyy.saas.payment.adpater.jd.common.JDConstants;
import com.xyy.saas.payment.adpater.pingxx.constants.OrderConstants;
import com.xyy.saas.payment.adpater.wxPayHandler.WxEntrustAdapterService;
import com.xyy.saas.payment.adpater.wxPayHandler.WxPayAdapterService;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.RocketMqTopic;
import com.xyy.saas.payment.common.constants.ChannelConstants;
import com.xyy.saas.payment.common.constants.MqConstant;
import com.xyy.saas.payment.common.enums.BusinessPayStatusEnum;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.bo.*;
import com.xyy.saas.payment.cores.enums.*;
import com.xyy.saas.payment.cores.param.TransactionParam;
import com.xyy.saas.payment.cores.vo.BusinessPayVo;
import com.xyy.saas.payment.dao.model.*;
import com.xyy.saas.payment.entrust.core.dto.EntrustPayDto;
import com.xyy.saas.payment.entrust.core.dto.EntrustPayInfoDto;
import com.xyy.saas.payment.payment.core.bo.RefundBO;
import com.xyy.saas.payment.payment.core.dto.OrderClosedDto;
import com.xyy.saas.payment.payment.core.dto.PayDto;
import com.xyy.saas.payment.payment.core.dto.PrepayDto;
import com.xyy.saas.payment.payment.core.dto.RefundDto;
import com.xyy.saas.payment.payment.core.service.PrePayOrderService;
import com.xyy.saas.payment.service.*;
import com.xyy.saas.payment.util.*;
import com.xyy.saas.payment.util.mq.MqProducer;
import com.xyy.util.Base64Util;
import com.xyy.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

/**
 * @Author zhangwenxiong
 * @Date 2020/11/24 16:48
 * @Version 1.0
 */
@Slf4j
@Service
@DependsOn("apolloUtil")
public class WxCommonAdpter implements PaymentAdapter {
    @Value("${ec.wx.pay.switch:true}")
    private Boolean ecWxPaySwitch;
    @Autowired
    private MqProducer mqProducer;

    @Autowired
    private RedisClient redisClient;

    @Autowired
    private PrePayOrderService prePayOrderService;

    @Autowired
    private ChannelSettingManager channelSettingManager;

    @Autowired
    private BusinessRecordService businessRecordService;
    @Autowired
    private BankConfigManager bankConfigManager;

    @Autowired
    private EntrustPayInfoService entrustPayInfoService;

    @Autowired
    private PayAccountService payAccountService;

    @Autowired
    private LeafUtil leafUtil;

    @Autowired
    private PayResultOrderService payResultOrderService;
    @Resource
    private ThirdUserInfoService thirdUserInfoService;

    @Value("${wx.pay.queryOrder}")
    private String wxQueryOrderUrl;
    @Value("${wx.pay.queryRefund}")
    private String wxQueryRefundUrl;

    private static final Logger LOGGER = LoggerFactory.getLogger(WxCommonAdpter.class);

    static Map<String, String> map = new HashMap<>();
    static {
        //微信 App 支付
        map.put(PayModeEnum.APP.getMode(),"APP");
        //微信 JSAPI 支付
        map.put(PayModeEnum.PUB.getMode(),"JSAPI");
        //微信 Native 支付
        map.put(PayModeEnum.QR.getMode(),"NATIVE");
        //微信付款码支付
        map.put("10","wx_pub_scan");
        //微信 H5 支付
        map.put(PayModeEnum.H5.getMode(),"MWEB");
        //微信小程序支付
        map.put(PayModeEnum.MINI.getMode(),"JSAPI");
    }

    @Override
    public PrepayBo prepay(PrepayDto prepayDto) throws PaymentException {
        return prePayOrderService.prepayOrderByPrepayDto(prepayDto);
    }

    @Override
    public RefundBO refund(RefundDto refundDto) throws PaymentException {
        RefundBO refundBO = new RefundBO();
        SortedMap<String, String> resultMap;
        Map<String, Object> packageParams = new TreeMap<>(
                new Comparator<String>() {
                    @Override
                    public int compare(String obj1, String obj2) {
                        // 升序排序
                        return obj1.compareTo(obj2);
                    }
                });
        try {
            boolean isFBank = bankConfigManager.isFuminBankAggregatePay(refundDto.getBusinessOrderType(), refundDto.getUserId(), refundDto.getPayMode());
            boolean isFBankPayMode = PayModeEnum.MINI.getMode().equals(refundDto.getPayMode()) || PayModeEnum.PUB.getMode().equals(refundDto.getPayMode());
            boolean isFBankReceiver = !Objects.equals(ApolloUtil.getProperty(Constants.POP_DIRECT_PAY_YBM_TECH_USER_ID), refundDto.getUserId());
            WxSettings wxSettings = channelSettingManager.getWXSettings(refundDto.getReceiverId(), refundDto.getBusinessOrderType(), refundDto.getPayMode());
            ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(refundDto.getUserId());

            // POP商户
            if("ec_pop".equals(refundDto.getBusinessOrderType()) && !StringUtils.isBlank(refundDto.getMerchantId()) &&
                    !Objects.equals(refundDto.getMerchantId(), wxSettings.getMerchantId()) && !"1629113578".equals(refundDto.getMerchantId()))
            {
                wxSettings = channelSettingManager.getWXSettingByMchId(refundDto.getMerchantId());

                if(isFBank && isFBankPayMode) {
                    wxSettings.setWxAppId(FBankPowerIdEnum.getAppIdInfo(refundDto.getBusinessOrderType(),
                            FBankPowerIdEnum.getPowerId(ChannelEnum.AGGREGATE_WX.getCode(), refundDto.getPayMode())).getKey());
                }
            }
            // 荷叶自营商户
            else if("yikuaiqian_doc".equals(refundDto.getBusinessOrderType()) && !StringUtils.isBlank(refundDto.getMerchantId()) &&
                    !Objects.equals(refundDto.getMerchantId(), wxSettings.getMerchantId()))
            {
                WxSettings tempWxSettings = channelSettingManager.getWXSettingByMchId(refundDto.getMerchantId());
                tempWxSettings.setWxAppId(wxSettings.getWxAppId());
                wxSettings = tempWxSettings;
            }
            // 已切平安商户
            else if(thirdUserInfo != null && Constants.PINGAN.equals(thirdUserInfo.getThirdType()) && "1629113578".equals(refundDto.getMerchantId())) {
                wxSettings = channelSettingManager.getWXSettings(refundDto.getReceiverId(), refundDto.getBusinessOrderType() + "_" + Constants.PINGAN, refundDto.getPayMode());
            }
            // 大商户已切平安
            else if(Boolean.parseBoolean(ApolloUtil.getProperty(Constants.EC_POP_BIG_SHOP_TO_PINGAN_SWITCH, Boolean.FALSE.toString())) &&
                    Objects.equals(ApolloUtil.getProperty(Constants.POP_DIRECT_PAY_YBM_TECH_USER_ID), refundDto.getUserId()) &&
                    PayModeEnum.MINI.getMode().equals(refundDto.getPayMode()) && "1629113578".equals(refundDto.getMerchantId()))
            {
                wxSettings = channelSettingManager.getWXSettings(refundDto.getReceiverId(), refundDto.getBusinessOrderType() + "_" + Constants.PINGAN, refundDto.getPayMode());
            }
            else if(isFBank && isFBankPayMode && isFBankReceiver) {
                wxSettings.setWxAppId(FBankPowerIdEnum.getAppIdInfo(refundDto.getBusinessOrderType(),
                        FBankPowerIdEnum.getPowerId(ChannelEnum.AGGREGATE_WX.getCode(), refundDto.getPayMode())).getKey());
            }

            LOGGER.info("WxCommonAdpter#refund refundDto={}, wxSettings={}", JSONObject.toJSONString(refundDto), JSONObject.toJSONString(wxSettings));

            // 富民小程序需要替换appid
            packageParams.put("appid", wxSettings.getWxAppId());
            packageParams.put("mch_id", wxSettings.getMerchantId());
            packageParams.put("nonce_str", WxUtil.getNonceStr());
            packageParams.put("out_trade_no", refundDto.getPayNo());
            packageParams.put("out_refund_no", refundDto.getRefundNo());
            packageParams.put("total_fee", refundDto.getTotalAmount());
            packageParams.put("refund_fee", refundDto.getAmount());
            packageParams.put("refund_desc", refundDto.getDescription());
            packageParams.put("notify_url", ApolloUtil.getProperty("wx.notifyUrl.refund.direct"));
            String xml = WxUtil.directMap2Xml(packageParams, wxSettings.getApiSecret());
            SSLConnectionSocketFactory sslsf = WxUtil.initKeyStoreByPem(wxSettings.getApiCertificate(), wxSettings.getPrivateKey(), wxSettings.getMerchantId());
            CloseableHttpClient httpclient = HttpClients.custom()
                    .setSSLSocketFactory(sslsf)
                    .build();
            HttpPost httpPost = new HttpPost(ApolloUtil.getProperty("wx.pay.refundUrl"));
            httpPost.setEntity(new StringEntity(xml, "UTF-8"));
            CloseableHttpResponse response = httpclient.execute(httpPost);

            String resultStr = "";
            if (response != null && response.getEntity() != null) {
                resultStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                response.close();
            }
            httpclient.close();
            resultMap = WxUtil.doXMLParse(resultStr);
            LOGGER.info("WxCommonAdpter#refund result pay_no:{},result:{}", refundDto.getPayNo(), JSON.toJSONString(resultMap));
        } catch (Exception e) {
            LOGGER.error("WxCommonAdpter refund error refundDto={}, {}", JSON.toJSON(refundDto), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }

        if ("SUCCESS".equals(resultMap.get("return_code")) && "SUCCESS".equals(resultMap.get("result_code"))) {
            refundBO.setRefundNo(refundDto.getRefundNo());
            refundBO.setChannelRefundNo(resultMap.get("refund_id"));
            refundBO.setStatus(RefundOrder.STATUS_IN);
            LOGGER.info("WxCommonAdpter#refund create success pay_no:{},result:{}", refundDto.getPayNo(), JSON.toJSONString(resultMap));
        } else if ("NOTENOUGH".equals(resultMap.get("return_code")) || "NOTENOUGH".equals(resultMap.get("result_code"))) {
            throw new PaymentException(ResultCodeEnum.CHANNEL_TODAY_MONEY_NOT_ENOUGH);
        } else {
            LOGGER.error("WxCommonAdpter#refund create failed refundDto:{},result:{}", JSON.toJSONString(refundDto), JSON.toJSONString(resultMap));
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }
        return refundBO;
    }

    @Override
    public PayBo pay(PayDto payDto) throws PaymentException {
        PayBo payBo = new PayBo();
        String channel = payDto.getChannel();
        String mode = payDto.getPayMode();
        PayBo temp = redisClient.get(payDto.getPayMode()+payDto.getPayNo()+channel, PayBo.class);
        if(temp != null) {
            LOGGER.info("WxCommonAdpter pay end get from redis appPayBo:{}", JSON.toJSONString(temp));
            return temp;
        }
        LOGGER.info("WxCommonAdpter pay begin params:{}", JSONUtils.toJSON(payDto));

        String businessOrderType = payDto.getBusinessOrderType();
        if (ecWxPaySwitch && StringUtils.isNotEmpty(businessOrderType) && businessOrderType.contains(Constants.EC_BUSINESS_ORDER_TYPE_PREFIX)) {
            LOGGER.error("WxCommonAdpter derict pay is closd payNo:{}, mode:{}", payDto.getPayNo(),mode);
            throw new PaymentException(ResultCodeEnum.WX_DERICT_CHANNEL_CLOSED);
        }

        Map<String, Object> reqMap = new TreeMap<String, Object>(
                new Comparator<String>() {
                    @Override
                    public int compare(String obj1, String obj2) {
                        // 升序排序
                        return obj1.compareTo(obj2);
                    }
                });
        boolean isFBank = bankConfigManager.isFuminBankAggregatePay(payDto.getBusinessOrderType(), payDto.getReceiverId(), payDto.getPayMode());
        boolean isFBankPayMode = PayModeEnum.MINI.getMode().equals(mode) || PayModeEnum.PUB.getMode().equals(mode);
        log.info("{} isFBank:{}, isFBankPayMode:{}", payDto.getBusinessOrderNo(), isFBank, isFBankPayMode);

        // 业务未切富民的收款方是11af
        boolean isFBankReceiver = !Objects.equals(ApolloUtil.getProperty(Constants.POP_DIRECT_PAY_YBM_TECH_USER_ID), payDto.getReceiverId());
        Map<String, String> wxParam = channelSettingManager.getWXParam(payDto.getReceiverId(), payDto.getBusinessOrderType(), payDto.getPayMode());

        if (Boolean.parseBoolean(ApolloUtil.getProperty(Constants.OTHER_PAY_WX_TRANSTOBEIJING, Boolean.FALSE.toString())) && null != payDto.getMap() && PaySceneEnum.OTHER_PAY.getCode().equals(payDto.getMap().getOrDefault("payScene", "")) && payDto.getBusinessOrderType().contains("ec")) {
            wxParam = channelSettingManager.getWXParam(payDto.getReceiverId(), "ec_pop", PayModeEnum.MINI.getMode());
            log.info("otherpay.wx.transtobeijing查的微信配置信息:{} {}", payDto.getBusinessOrderNo(), JSON.toJSONString(wxParam));
        } else {
            /**
             * 开关打开且POP商户
             */
            if (Boolean.parseBoolean(ApolloUtil.getProperty("ec_pop_switch_ybmmch")) && "ec_pop".equals(payDto.getBusinessOrderType())) {
                Map<String, String> ybmwxParam = channelSettingManager.getWXParam(payDto.getReceiverId(), "ec_self_support_mini_XS140001", "pub");
                wxParam.put("mch_id", ybmwxParam.get("mch_id"));
                wxParam.put("apiKey", ybmwxParam.get("apiKey"));

                if (isFBank && PayModeEnum.MINI.getMode().equals(mode)) {
                    wxParam.put("appid", FBankPowerIdEnum.getAppIdInfo(payDto.getBusinessOrderType(), FBankPowerIdEnum.WX_MINI).getKey());
                    log.info("isFBnak为true并且payMode为MINI:{} {}", payDto.getBusinessOrderNo(), JSON.toJSONString(wxParam));
                }
            }
        }

        /**
         * 开关打开且荷叶自营商户
         */
        if(Boolean.parseBoolean(ApolloUtil.getProperty("heye_switch_pay_merchant")) && "yikuaiqian_doc".equals(payDto.getBusinessOrderType())) {
            Map<String, String> heyeParam = channelSettingManager.getWXParam(payDto.getReceiverId(), "yikuaiqian_doc_gk", "mini");
            wxParam.put("mch_id", heyeParam.get("mch_id"));
            wxParam.put("apiKey", heyeParam.get("apiKey"));
        }

        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(payDto.getReceiverId());

        /**
         * POP已切平安商户
         */
        if(thirdUserInfo != null && Constants.PINGAN.equals(thirdUserInfo.getThirdType())) {
            wxParam = channelSettingManager.getWXParam(payDto.getReceiverId(), payDto.getBusinessOrderType() + "_" + Constants.PINGAN, payDto.getPayMode());
        }
        // POP大商户已切平安
        else if(Boolean.parseBoolean(ApolloUtil.getProperty(Constants.EC_POP_BIG_SHOP_TO_PINGAN_SWITCH, Boolean.FALSE.toString())) &&
                Objects.equals(ApolloUtil.getProperty(Constants.POP_DIRECT_PAY_YBM_TECH_USER_ID), payDto.getReceiverId()) &&
                PayModeEnum.MINI.getMode().equals(payDto.getPayMode()))
        {
            wxParam = channelSettingManager.getWXParam(payDto.getReceiverId(), payDto.getBusinessOrderType() + "_" + Constants.PINGAN, payDto.getPayMode());
        }
        else if(isFBank && isFBankPayMode && isFBankReceiver) {
            wxParam.put("appid", FBankPowerIdEnum.getAppIdInfo(payDto.getBusinessOrderType(), FBankPowerIdEnum.getPowerId(channel, mode)).getKey());
        }

        LOGGER.info("WxCommonAdpter pay begin params and mch:{}, wxParam:{}", JSONUtils.toJSON(payDto), JSONUtils.toJSON(wxParam));

        try {
            if (payDto.getChannel().startsWith(Constants.CHANNEL_JDAGGREGATE_NAME)) {
                wxParam.put("appid", JDConstants.aggregateWxAppID("WxCommonAdpter.pay", payDto.getBusinessType()));
            }
            reqMap.put("mch_id", wxParam.get("mch_id"));
            reqMap.put("appid", wxParam.get("appid"));
            reqMap.put("nonce_str", WxUtil.getNonceStr());
            //业务方传
            reqMap.put("body", payDto.getMap().get("body"));
            reqMap.put("out_trade_no", payDto.getPayNo());
            //订单总金额，单位为分
            reqMap.put("total_fee", payDto.getAmount());
            //用户端ip
            String clientIp = payDto.getMap().get(OrderConstants.CLIENT_IP)==null?Constants.DEFAULT_CLIENT_IP:payDto.getMap().get(OrderConstants.CLIENT_IP).toString();
            reqMap.put("spbill_create_ip", clientIp);
            reqMap.put("notify_url", ApolloUtil.getProperty("wx.notifyUrl.pay.direct"));

            //将不同参数拼装到map中
            WxPayAdapterService.getInstance().invoker(mode,reqMap,payDto.getMap());

            if (payDto.getMap() != null && payDto.getMap().containsKey("timeExpire") && !PayModeEnum.H5.getMode().equals(mode)) {
                reqMap.put("time_expire", payDto.getMap().get("timeExpire"));
            }
            Map<String, String> attachMap = new HashMap<>();
            attachMap.put("business_order_type", payDto.getBusinessOrderType());
            attachMap.put("pay_mode", mode);
            reqMap.put("attach", JSONUtils.toJSON(attachMap));
            LOGGER.info(mode+"WxCommonAdpter pay reqMap:{}", JSONObject.toJSONString(reqMap));
            //构建XML
            String reqStr = WxUtil.directMap2Xml(reqMap, wxParam.get("apiKey"));
            String wxUrl = ApolloUtil.getProperty("wx.pay.payUrl");
            String resultXml = HttpClientUtil.xyyHttpPostRequest(wxUrl, reqStr, "application/xml");
            Map<String, String> resultParamsMap = WxUtil.doXMLParse(resultXml);
            LOGGER.info(mode+"WxCommonAdpter pay return result resultXml:{}", JSONObject.toJSONString(resultParamsMap));

            //解析微信返回串 如果状态成功 则返回给前端
            //解析微信请求结果，如果成功返回给前端
            if (resultParamsMap.get("return_code") == null || !"SUCCESS".equals(resultParamsMap.get("return_code"))
                    || resultParamsMap.get("result_code") == null || !"SUCCESS".equals(resultParamsMap.get("result_code"))) {
                LOGGER.error("WxCommonAdpter pay wechat post error payNo:{}, message:{}", payDto.getPayNo(), JSONUtils.toJSON(resultParamsMap));
                throw new PaymentException(resultParamsMap.get("err_code"), resultParamsMap.get("err_code_des"));
            }

            if (StringUtil.isEmpty(resultParamsMap.get("prepay_id"))) {
                LOGGER.error(mode+"WxCommonAdpter pay wechat post error payNo:{}, message:{}", payDto.getPayNo(), JSONUtils.toJSON(resultParamsMap));
                throw new PaymentException(ResultCodeEnum.THIRD_PARAM_ERROR);
            }

            payBo.setPayNO(payDto.getPayNo());
            String payResultMode = "";
            if(StringUtils.isNotBlank(ApolloUtil.getProperty("wx.pay.result.basic"))) {
                List<String> whiteList = JSONUtils.parse2List(ApolloUtil.getProperty("wx.pay.result.basic"), String.class);
                if(whiteList.contains(payDto.getBusinessOrderType())) {
                    payResultMode = payDto.getPayResultMode() == null? "" : payDto.getPayResultMode();
                }
            }
            payBo.setPayLoad(WxPayAdapterService.getInstance().invokerResult(mode+"Result"+payResultMode,wxParam,resultParamsMap));
            queryPayState(payDto.getPayNo(), payDto.getBusinessOrderType(), "pay");
            LOGGER.info(mode+"WxCommonAdpter pay Call wechat unified order interface succeeded orderCid:{},result:{}", payDto.getPayNo(), payBo.getPayLoad());
        }
        catch (PaymentException ex) {
            LOGGER.error(mode+"WxCommonAdpter pay Wechat unified order failed payDto:{}", JSON.toJSON(payDto), ex);
            throw ex;
        }
        catch (Exception e){
            LOGGER.error(mode+"WxCommonAdpter pay Wechat unified order failed payDto:{}", JSON.toJSON(payDto), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR.getCode(), ResultCodeEnum.CHANNEL_CONNECT_ERROR.getMsg());
        }
        //订单关闭调接口参数
        redisClient.setEx(Constants.ORDER_CLOSE_KEY+payDto.getChannel()+payDto.getPayNo(),payDto.getBusinessOrderType()+":"+payDto.getPayMode(),86400);
        //订单支付参数
        redisClient.set(payDto.getPayMode()+payDto.getPayNo()+channel, payBo,PayTimeExpireUtil.handleTimeExpire(channel, mode,payDto.getTimeExpire()));
        return payBo;
    }

    @Override
    public TransactionResultBO queryTransaction(TransactionParam param) throws PaymentException {
        if(TransactionTypeEnum.PAY.getCode().equals(param.getTransactionType())) {
            TransactionResultBO transactionResultBO = queryPay(param);
            transactionResultBO.setChannel(ChannelConstants.CHANNEL_DIRECT_NAME+"_"+ChannelConstants.CHANNEL_WX_NAME);
            return transactionResultBO;
        }else if(TransactionTypeEnum.REFUND.getCode().equals(param.getTransactionType())) {
            return queryRefund(param);
        }else{
            throw new PaymentException(ResultCodeEnum.UN_SUPPORT_METHOD);
        }
    }

    private TransactionResultBO queryPay(TransactionParam param) throws PaymentException {
        boolean isFBank = bankConfigManager.isFuminBankAggregatePay(param.getBusinessOrderType());
        Map<String, String> wxParam = channelSettingManager.getWXParam(param.getAccountId(), param.getBusinessOrderType(), param.getPayMode());

        /**
         * 开关打开且POP商户
         */
        if(Boolean.parseBoolean(ApolloUtil.getProperty("ec_pop_switch_ybmmch")) && "ec_pop".equals(param.getBusinessOrderType())) {
            Map<String, String> ybmwxParam = channelSettingManager.getWXParam(param.getAccountId(), "ec_self_support_mini_XS140001", "pub");
            wxParam.put("mch_id", ybmwxParam.get("mch_id"));
            wxParam.put("apiKey", ybmwxParam.get("apiKey"));

            if(isFBank && PayModeEnum.MINI.getMode().equals(param.getPayMode())) {
                wxParam.put("appid", FBankPowerIdEnum.getAppIdInfo(param.getBusinessOrderType(), FBankPowerIdEnum.WX_MINI).getKey());
            }
        }

        /**
         * 开关打开且荷叶自营商户
         */
        if(Boolean.parseBoolean(ApolloUtil.getProperty("heye_switch_pay_merchant")) && "yikuaiqian_doc".equals(param.getBusinessOrderType())) {
            Map<String, String> heyeParam = channelSettingManager.getWXParam(param.getAccountId(), "yikuaiqian_doc_gk", "mini");
            wxParam.put("mch_id", heyeParam.get("mch_id"));
            wxParam.put("apiKey", heyeParam.get("apiKey"));
        }
        TransactionResultBO item = new TransactionResultBO();
        item.setMerchantId(wxParam.get("mch_id"));
        try {
            Map<String, Object> reqMap = new TreeMap<String, Object>(
                    new Comparator<String>() {
                        @Override
                        public int compare(String obj1, String obj2) {
                            // 升序排序
                            return obj1.compareTo(obj2);
                        }
                    });
            reqMap.put("appid", wxParam.get("appid"));
            reqMap.put("mch_id", wxParam.get("mch_id"));
            reqMap.put("out_trade_no", param.getTradeNo());
            reqMap.put("nonce_str", WxUtil.getNonceStr());
            String reqStr = WxUtil.directMap2Xml(reqMap, wxParam.get("apiKey"));
            String resultXml = HttpClientUtil.xyyHttpPostRequest(wxQueryOrderUrl, reqStr, "application/xml");
            Map<String, String> resultParamsMap = WxUtil.doXMLParse(resultXml);
            log.info("WxcommonAdapter#queryPay reqStr:{}, resultMap:{}", reqStr, JSONUtils.toJSON(resultParamsMap));
            if("SUCCESS".equals(resultParamsMap.get("return_code"))) {
                if("SUCCESS".equals(resultParamsMap.get("result_code"))) {
                    if ("SUCCESS".equals(resultParamsMap.get("trade_state"))) {
                        item.setSuccessTime(DateUtil.dateToLong(DateUtil.parase(resultParamsMap.get("time_end"), DateUtil.YYYYMMDDHHMMSS)));
                        item.setTradeNo(param.getTradeNo());
                        item.setChannelChargeNo(resultParamsMap.get("transaction_id"));
                        item.setStatus(TransactionStatusEnum.SUCCESS.getCode());
                        item.setAmount(Integer.valueOf(resultParamsMap.get("total_fee")));
                        item.setChannel(ChannelConstants.CHANNEL_DIRECT_NAME+"_"+ChannelConstants.CHANNEL_WX_NAME);
                        return item;
                    } else if ("PAYERROR".equals(resultParamsMap.get("trade_state"))) {
                        item.setTradeNo(param.getTradeNo());
                        item.setStatus(TransactionStatusEnum.FAIL.getCode());
                        return item;
                        // TODO 就测试来说，支付成功退款了会返回这个状态，但是未测试支付未成功退汇情况
                    } else if("REFUND".equals(resultParamsMap.get("trade_state"))) {
                        // 订单发生过退款，退款详情请查询退款
                        item.setSuccessTime(DateUtil.dateToLong(DateUtil.parase(resultParamsMap.get("time_end"), DateUtil.YYYYMMDDHHMMSS)));
                        item.setTradeNo(param.getTradeNo());
                        item.setChannelChargeNo(resultParamsMap.get("transaction_id"));
                        item.setStatus(TransactionStatusEnum.REFUNDED.getCode());
                        item.setAmount(Integer.valueOf(resultParamsMap.get("total_fee")));
                        return item;
                    } else {
                        item.setTradeNo(param.getTradeNo());
                        item.setStatus(TransactionStatusEnum.TRANSACTING.getCode());
                        return item;
                    }
                }else{
                    if("ORDERNOTEXIST".equals(resultParamsMap.get("err_code"))){
                        throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
                    }
                    throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
                }
            }else{
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
            }
        } catch (PaymentException e) {
            log.error("WxCommonAdpter#queryRefund exception. ", e);
            throw e;
        } catch (Exception e) {
            log.error("WxCommonAdpter#queryPay error. ", e);
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    private TransactionResultBO queryRefund(TransactionParam param) throws PaymentException {
        boolean isFBank = bankConfigManager.isFuminBankAggregatePay(param.getBusinessOrderType());
        Map<String, String> wxParam = channelSettingManager.getWXParam(param.getAccountId(), param.getBusinessOrderType(), param.getPayMode());

        /**
         * 开关打开且POP商户
         */
        if(Boolean.parseBoolean(ApolloUtil.getProperty("ec_pop_switch_ybmmch")) && "ec_pop".equals(param.getBusinessOrderType())) {
            Map<String, String> ybmwxParam = channelSettingManager.getWXParam(param.getAccountId(), "ec_self_support_mini_XS140001", "pub");
            wxParam.put("mch_id", ybmwxParam.get("mch_id"));
            wxParam.put("apiKey", ybmwxParam.get("apiKey"));

            if(isFBank && PayModeEnum.MINI.getMode().equals(param.getPayMode())) {
                wxParam.put("appid", FBankPowerIdEnum.getAppIdInfo(param.getBusinessOrderType(), FBankPowerIdEnum.WX_MINI).getKey());
            }
        }

        /**
         * 开关打开且荷叶自营商户
         */
        if(Boolean.parseBoolean(ApolloUtil.getProperty("heye_switch_pay_merchant")) && "yikuaiqian_doc".equals(param.getBusinessOrderType())) {
            Map<String, String> heyeParam = channelSettingManager.getWXParam(param.getAccountId(), "yikuaiqian_doc_gk", "mini");
            wxParam.put("mch_id", heyeParam.get("mch_id"));
            wxParam.put("apiKey", heyeParam.get("apiKey"));
        }
        TransactionResultBO item = new TransactionResultBO();
        item.setMerchantId(wxParam.get("mch_id"));
        try {
            Map<String, Object> reqMap = new TreeMap<String, Object>(
                    new Comparator<String>() {
                        @Override
                        public int compare(String obj1, String obj2) {
                            // 升序排序
                            return obj1.compareTo(obj2);
                        }
                    });
            reqMap.put("appid", wxParam.get("appid"));
            reqMap.put("mch_id", wxParam.get("mch_id"));
            reqMap.put("nonce_str", WxUtil.getNonceStr());
            reqMap.put("out_refund_no", param.getTradeNo());
            String reqStr = WxUtil.directMap2Xml(reqMap, wxParam.get("apiKey"));
            String resultXml = HttpClientUtil.xyyHttpPostRequest(wxQueryRefundUrl, reqStr, "application/xml");
            Map<String, String> resultParamsMap = WxUtil.doXMLParse(resultXml);
            log.info("WxcommonAdapter#queryRefund reqStr:{}, resultMap:{}", reqStr, JSONUtils.toJSON(resultParamsMap));
            if("SUCCESS".equals(resultParamsMap.get("return_code"))) {
                if("SUCCESS".equals(resultParamsMap.get("result_code"))) {
                    // TODO 暂时不支持查询多个部分退
                    if ("SUCCESS".equals(resultParamsMap.get("refund_status_0"))) {
                        item.setSuccessTime(DateUtil.dateToLong(DateUtil.parase(resultParamsMap.get("refund_success_time_0"), DateUtil.DATEFORMATSECOND)));
                        item.setTradeNo(param.getTradeNo());
                        item.setChannelChargeNo(resultParamsMap.get("transaction_id"));
                        item.setStatus(TransactionStatusEnum.SUCCESS.getCode());
                        item.setAmount(Integer.valueOf(resultParamsMap.get("total_fee")));
                        return item;
                    } else if ("REFUNDCLOSE".equals(resultParamsMap.get("refund_status_0"))) {
                        // 退款关闭
                        item.setTradeNo(param.getTradeNo());
                        item.setStatus(TransactionStatusEnum.FAIL.getCode());
                        return item;
                    } else if ("PROCESSING".equals(resultParamsMap.get("refund_status_0"))){
                        item.setTradeNo(param.getTradeNo());
                        item.setStatus(TransactionStatusEnum.TRANSACTING.getCode());
                        return item;
                    }  else if ("CHANGE".equals(resultParamsMap.get("refund_status_0"))){
                        // 退款异常
                        item.setTradeNo(param.getTradeNo());
                        item.setStatus(TransactionStatusEnum.TRANSACTING.getCode());
                        return item;
                    } else {
                        // 其他情况
                        item.setTradeNo(param.getTradeNo());
                        item.setStatus(TransactionStatusEnum.TRANSACTING.getCode());
                        return item;
                    }
                }else{
                    if("REFUNDNOTEXIST".equals(resultParamsMap.get("err_code"))){
                        throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
                    }
                    throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
                }
            }else{
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
            }
        } catch (PaymentException e) {
            log.error("WxCommonAdpter#queryRefund exception. ", e);
            throw e;
        } catch (Exception e) {
            log.error("WxCommonAdpter#queryRefund error. ", e);
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    @Override
    public OrderClosedBo orderClosed(OrderClosedDto orderClosedDto) throws PaymentException {
        OrderClosedBo closedBo = new OrderClosedBo();
        //从redis获取发起支付时的缓存,用于下面查询微信配置
        String typeAndMode = redisClient.get(Constants.ORDER_CLOSE_KEY+orderClosedDto.getChannel()+orderClosedDto.getPayNo());
        if (StringUtils.isEmpty(typeAndMode)){
            LOGGER.info("WxCommonAdpter orderClosed end get from redis typeAndMode:{},orderClosedDto:{}", JSON.toJSONString(typeAndMode),JSON.toJSONString(orderClosedDto));
            throw new PaymentException(ResultCodeEnum.ORDER_NOT_PAID_ERROR);
        }

        //从redis取出的缓存格式是type:mode
        String[] arr = typeAndMode.split(":");
        LOGGER.info("WxCommonAdpter orderClosed begin typeAndMode:{},orderClosedDto:{}", typeAndMode,JSON.toJSONString(orderClosedDto));
        Map<String, Object> reqMap = new TreeMap<String, Object>(
                new Comparator<String>() {
                    @Override
                    public int compare(String obj1, String obj2) {
                        // 升序排序
                        return obj1.compareTo(obj2);
                    }
                });
        // 查询预支付单
        PrePayOrder prePayOrderByPayNo = prePayOrderService.getPrePayOrderByPayNo(orderClosedDto.getPayNo());
        //查询微信配置
        boolean isFBank = bankConfigManager.isFuminBankAggregatePay(orderClosedDto.getBusinessOrderType());
        Map<String, String> wxParam = channelSettingManager.getWXParam(null == prePayOrderByPayNo ? "" : prePayOrderByPayNo.getReceiverId(), arr[0], arr[1]);
        try {
            //构建微信调用参数
            reqMap.put("mch_id", wxParam.get("mch_id"));
            // 富民小程序需要替换appid
            reqMap.put("appid", isFBank && PayModeEnum.MINI.getMode().equals(arr[1]) ? ApolloUtil.getProperty("fbank_mini_wx_appid") : wxParam.get("appid"));
            reqMap.put("nonce_str", WxUtil.getNonceStr());
            reqMap.put("out_trade_no", orderClosedDto.getPayNo());
            //构建XML
            String reqStr = WxUtil.directMap2Xml(reqMap, wxParam.get("apiKey"));
            String wxUrl = "https://api.mch.weixin.qq.com/pay/closeorder";
            String resultXml = HttpClientUtil.xyyHttpPostRequest(wxUrl, reqStr, "application/xml");
            LOGGER.info("WxCommonAdpter orderClosed return result resultXml:{}", Base64Util.Bytes2Base64(resultXml.getBytes()));
            Map<String, String> resultParamsMap = WxUtil.doXMLParse(resultXml);

            //解析微信返回串 如果状态成功 则返回给前端
            //解析微信请求结果，如果成功返回给前端
            if (resultParamsMap.get("return_code") == null || !"SUCCESS".equals(resultParamsMap.get("return_code"))
                    || resultParamsMap.get("result_code") == null || !"SUCCESS".equals(resultParamsMap.get("result_code"))) {
                LOGGER.error("WxCommonAdpter orderClosed wechat post error payNo:{}, message:{}", orderClosedDto.getPayNo(), JSONUtils.toJSON(resultParamsMap));
                throw new PaymentException(ResultCodeEnum.THIRD_PARAM_ERROR);
            }

            closedBo.setPayNo(orderClosedDto.getPayNo());
            closedBo.setStatus(OrderClosedBo.SUCCESS);
            LOGGER.info("WxCommonAdpter orderClosed Call wechat unified order interface succeeded orderCid:{},result:{}", orderClosedDto.getPayNo(), closedBo.getStatus());
        }catch (Exception e){
            LOGGER.error("WxCommonAdpter orderClosed Wechat unified order failed orderClosedDto:{}", JSON.toJSON(orderClosedDto), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR.getCode(), ResultCodeEnum.CHANNEL_CONNECT_ERROR.getMsg());
        }
        return closedBo;
    }

    /**
     * 签约
     *
     * @param entrustPayInfoDto
     * @return
     */
    @Override
    public EntrustBO entrustSign(EntrustPayInfoDto entrustPayInfoDto) throws PaymentException {
        LOGGER.info("WxCommonAdpter entrustSign begin params:{}", JSONUtils.toJSON(entrustPayInfoDto));

        // 查询该签约是否存在
        EntrustPayInfo param = new EntrustPayInfo();
        param.setSignUserId(entrustPayInfoDto.getSignUserId());
        param.setSignOrderNo(entrustPayInfoDto.getSignOrderNo());
        param.setSignMode(entrustPayInfoDto.getSignMode());
        // 正在签约中
        param.setStatus(EntrustStatusEnum.IN_ENTRUST.getCode());
        List<EntrustPayInfo> inEntrustList = entrustPayInfoService.selectByParam(param);
        // 签约完成
        param.setStatus(EntrustStatusEnum.ENTRUST_SUCCESS.getCode());
        List<EntrustPayInfo> entrustSuccessList = entrustPayInfoService.selectByParam(param);
        if(!inEntrustList.isEmpty() || !entrustSuccessList.isEmpty()) {
            if(!inEntrustList.isEmpty() && !entrustSuccessList.isEmpty()) {
                throw new PaymentException("同时存在签约中和签约完成信息!");
            }
            EntrustPayInfo info = inEntrustList.isEmpty() ? entrustSuccessList.get(0) : inEntrustList.get(0);
            EntrustBO entrustBO = new EntrustBO();
            entrustBO.setEntrustUrl(genEntrustUrl(info, entrustPayInfoDto.getClientIp()));
            entrustBO.setContractCode(info.getContractCode());
            return entrustBO;
        }

        // 签约参数补全
        String contractCode = leafUtil.getSnowflakeId();
        if (contractCode == null) {
            log.error("WxCommonAdpter entrust get id from leaf is error entrustPayInfo:{}", JSON.toJSON(entrustPayInfoDto));
            throw new PaymentException(ResultCodeEnum.SYSTEM_BUSY_ERROR);
        }
        entrustPayInfoDto.setContractCode(contractCode);
        entrustPayInfoDto.setStatus(EntrustStatusEnum.IN_ENTRUST.getCode());
        entrustPayInfoDto.setCreateTime(DateUtil.now());
        entrustPayInfoDto.setVersion(1);
        entrustPayInfoDto.setYn(0);

        EntrustPayInfo entrustPayInfo = new EntrustPayInfo();
        BeanUtils.copyProperties(entrustPayInfoDto, entrustPayInfo);
        // 插入签约信息
        EntrustPayInfo resultInfo = entrustPayInfoService.insertSelective(entrustPayInfo);

        // 封装参数返回
        EntrustBO entrustBO = new EntrustBO();
        entrustBO.setEntrustUrl(genEntrustUrl(resultInfo, entrustPayInfoDto.getClientIp()));
        entrustBO.setContractCode(resultInfo.getContractCode());
        return entrustBO;
    }


    /** 通过签约信息生成 微信签约地址 */
    private String genEntrustUrl(EntrustPayInfo entrustPayInfo, String clientIp) throws PaymentException {
        // 签约方式
        PayModeEnum payMode = PayModeEnum.getEnum(entrustPayInfo.getSignMode());
        if(null == payMode) {
            throw new PaymentException("pay mode is error. entrustPayInfo:{}", JSONUtils.toJSON(entrustPayInfo));
        }
        PayAccount payAccount = payAccountService.getPayAccountByAccountId(entrustPayInfo.getAccountId());
        if(null == payAccount) {
            throw new PaymentException("pay account is not exist. entrustPayInfo:{}", JSONUtils.toJSON(entrustPayInfo));
        }
        WxSettings wxSettings = channelSettingManager.getWXSettings(payAccount.getAccountId(), payAccount.getBusinessIdType(), entrustPayInfo.getSignMode());
        if(null == wxSettings) {
            throw new PaymentException("wx settings is not exist!");
        }

        String requestSerial = leafUtil.getSnowflakeId();
        Map<String, Object> param = new HashMap<>();
        param.put("requestSerial", requestSerial);
        param.put("entrustPayInfo", entrustPayInfo);
        param.put("wxSettings", wxSettings);
        param.put("clientIp", clientIp);

        String entrustUrl = "";
        try {
            // 通过签约模式获取签约URL
            entrustUrl = WxEntrustAdapterService.getInstance().invokerEntrustUrl(payMode.getMode(), param);
        } catch (Exception e) {
            log.error("WxCommonAdapter#entrustSign {} invokerEntrustUrl get url error:{}",payMode.getMode(), e);
            return entrustUrl;
        }
        return entrustUrl;
    }


    /**
     * 签约后代扣
     *
     * @param entrustPayDto
     * @return
     */
    @Override
    public EntrustPayBO entrustPay(EntrustPayDto entrustPayDto) throws PaymentException {
        LOGGER.info("WxCommonAdpter entrustPay begin params:{}", JSONUtils.toJSON(entrustPayDto));
        EntrustPayBO payBO = new EntrustPayBO();
        //校验是否创建过订单
        PrePayOrder prePayOrder = prePayOrderService.getPrePayOrderByPayNo(entrustPayDto.getPayNo());
        if (prePayOrder == null) {
            LOGGER.warn("WxCommonAdpter entrust pay prePayOrder is not exist. payNo:{}", entrustPayDto.getPayNo());
            throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
        }
        payBO.setPayNO(prePayOrder.getPayNo());

        // 校验预支付订单的协议号是否合理
        EntrustPayInfo entrustPayInfo = entrustPayInfoService.selectByContractCode(prePayOrder.getContractCode());
        if(null == entrustPayInfo || entrustPayInfo.getStatus() != EntrustStatusEnum.ENTRUST_SUCCESS.getCode()) {
            LOGGER.error("WxCommonAdpter entrust pay error. prepayOrderInfo's entrustInfo is not exist. prePayOrder's contract code:{}", prePayOrder.getContractCode());
            throw new PaymentException(ResultCodeEnum.ENTRUST_NOT_EXIT_ERROR);
        }

        // 申请扣款
        try {
            papPayApply(entrustPayDto, entrustPayInfo.getContractCode());
        } catch (Exception e) {
            LOGGER.error("WxCommonAdpter entrust pay fail. error msg:{}", e);
        }
        return payBO;
    }

    /**
     * 微信代扣申请扣款-发送申请扣款
     */
    private void papPayApply(EntrustPayDto entrustPayDto, String contractCode) throws Exception {
        EntrustPayInfo entrustPayInfo = entrustPayInfoService.selectByContractCode(contractCode);
        if(null == entrustPayInfo) {
            throw new PaymentException("无签约信息，扣款失败!");
        }

        // 发起微信扣款
        try {
            PayAccount payAccount = payAccountService.getPayAccountByAccountId(entrustPayInfo.getAccountId());
            if(null == payAccount) {
                log.info("WxCommonAdpter entrustPay account info is not found. entrustInfo:{}", JSONUtils.toJSON(entrustPayInfo));
                throw new PaymentException("无收款方信息!");
            }
            //签名生成
            Map<String, Object> signParam = new TreeMap<>( (obj1, obj2) -> obj1.compareTo(obj2));
            WxSettings wxSettings = channelSettingManager.getWXSettings(payAccount.getAccountId(), payAccount.getBusinessIdType(), entrustPayDto.getPayMode());
            if(null == wxSettings) {
                throw new PaymentException("微信配置不存在!");
            }
            signParam.put("appid", wxSettings.getWxAppId());
            signParam.put("mch_id", wxSettings.getMerchantId());
            signParam.put("nonce_str", WxUtil.getNonceStr());
            signParam.put("body", entrustPayDto.getBody());
            signParam.put("out_trade_no", entrustPayDto.getPayNo());
            signParam.put("total_fee", entrustPayDto.getAmount());
            signParam.put("notify_url", ApolloUtil.getProperty("wx.notifyUrl.pap.pay"));
            Map<String, String>  attch = new HashMap<>();
            attch.put("business_type", entrustPayDto.getBusinessType());
            attch.put("receiver_id", entrustPayInfo.getAccountId());
            attch.put("callback_address", entrustPayDto.getCallback());
            attch.put("pay_mode", entrustPayDto.getPayMode());
            signParam.put("attach", JSONUtils.toJSON(attch));
            signParam.put("trade_type", "PAP");
            signParam.put("contract_id", entrustPayInfo.getContractId());
            String reqStr = WxUtil.directMap2Xml(signParam, wxSettings.getApiSecret());
            String wxUrl = ApolloUtil.getProperty("wx.entrust.applyPay");
            LOGGER.info(entrustPayDto.getPayMode()+" WxCommonAdpter request pay url:{}, param:{}", wxUrl, reqStr);
            String resultXml = HttpClientUtil.xyyHttpPostRequest(wxUrl, reqStr, "application/xml");
            LOGGER.info(entrustPayDto.getPayMode()+" WxCommonAdpter pay return result resultXml:{}", Base64Util.Bytes2Base64(resultXml.getBytes()));
            Map<String, String> resultParamsMap = WxUtil.doXMLParse(resultXml);
            String resultCode = resultParamsMap.get("return_code");
            if(!"SUCCESS".equalsIgnoreCase(resultCode)){
                LOGGER.info(entrustPayDto.getPayMode()+" WxCommonAdpter pay fail, error msg:{}", resultParamsMap.get("return_msg"));
                throw new PaymentException("微信扣款失败:"+resultParamsMap.get("return_msg"));
            }else {
                if("FAIL".equalsIgnoreCase(resultParamsMap.get("result_code"))) {
                    LOGGER.info(entrustPayDto.getPayMode()+" WxCommonAdpter pay fail, payNo:{}, error msg:{}", entrustPayDto.getPayNo(), resultParamsMap.get("err_code_des"));
                    throw new PaymentException("微信扣款失败:"+resultParamsMap.get("err_code_des"));
                }
            }
        } catch (Exception e) {
            LOGGER.info(entrustPayDto.getPayMode()+" WxCommonAdpter pay fail, error msg:{}", e.getMessage());
            throw new PaymentException(e);
        }
    }


    /**
     * 查询订单的支付状态，只有在回调接口关闭的时候会开启此功能
     */
    private void queryPayState(String tradeNo, String businessOrderType, String payType) {
        boolean isQueryPayState = Boolean.parseBoolean(ApolloUtil.getProperty("isQueryPayState"));
        if (isQueryPayState) {
            LOGGER.info("WxCommonAdpter queryPayState send query order state mq tradeNo:{}", tradeNo);
            Map<String, String> mqMsg = new HashMap<>(8);
            mqMsg.put("type", Constants.CHANNEL_WX_NAME);
            mqMsg.put("tradeNo", tradeNo);
            mqMsg.put("businessOrderType", businessOrderType);
            mqMsg.put("payType", payType);
            mqMsg.put("times", "0");
            mqProducer.syncSendDelayMsg(RocketMqTopic.TOPIC_QUERY_ORDER_STATE, JSON.toJSONString(mqMsg), MqConstant.DELAY_LEVELS[Integer.parseInt(ApolloUtil.getProperty("query.order.state.delay.level"))]);
        }
    }

    public String getPayModeByTradeType(String tradeType) throws PaymentException {
        for(String key : map.keySet()) {
            if(map.get(key).equals(tradeType)) {
                return key;
            }
        }
        throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
    }

    public List<String> getPayModesByTradeType(String tradeType) {
        List<String> modes = new ArrayList<>();
        for(String key : map.keySet()) {
            if(map.get(key).equals(tradeType)) {
                modes.add(key);
            }
        }
       return modes;
    }


    public BusinessPayBo sendPackage(BusinessPayVo payDto) throws PaymentException {
        BusinessPayBo payBo = null;
        SortedMap<String, String> resultMap;

        Map<String, Object> reqMap = new TreeMap<String, Object>(
                new Comparator<String>() {
                    @Override
                    public int compare(String obj1, String obj2) {
                        // 升序排序
                        return obj1.compareTo(obj2);
                    }
                });
        WxSettings wxSettings = channelSettingManager.getWXSettings("", payDto.getBusinessOrderType(), payDto.getPayMode());

        if(Boolean.parseBoolean(ApolloUtil.getProperty("heye_switch_redpack_merchant")) && "saas_pack".equals(payDto.getBusinessOrderType())) {
            WxSettings tempWxSettings = channelSettingManager.getWXSettings("", "yikuaiqian_fm", "mini");
            tempWxSettings.setWxAppId(wxSettings.getWxAppId());
            tempWxSettings.setBusinessOrderType(wxSettings.getBusinessOrderType());
            tempWxSettings.setPayMode(wxSettings.getPayMode());
            wxSettings = tempWxSettings;
        }

        LOGGER.info("WxCommonAdpter#sendPackage payDto={}, wxSettings={}", JSONObject.toJSONString(payDto), JSONObject.toJSONString(wxSettings));

        //新增记录
        BusinessRecordPo po = VoToPo(payDto,wxSettings);
        payBo = isExit(po);
        if(payBo != null) {
            if (BusinessPayStatusEnum.CREATE.getCode().equals(payBo.getLoad())){
                payDto.setPayNo(payBo.getBusinessNo());
            }else {
                LOGGER.info("WxCommonAdpter sendPackage end get from db payBo:{}", JSON.toJSONString(payBo));
                return payBo;
            }
        }
        try {
            reqMap.put("mch_id", wxSettings.getMerchantId());
            reqMap.put("wxappid", wxSettings.getWxAppId());
            reqMap.put("nonce_str", WxUtil.getNonceStr());
            //活动名称
            reqMap.put("act_name", payDto.getActName());
            reqMap.put("mch_billno", payDto.getPayNo());
            //订单总金额，单位为分
            reqMap.put("total_amount", payDto.getAmount());
            reqMap.put("re_openid", payDto.getOpenId());
            //用户端ip
            reqMap.put("client_ip", "192.168.1.75");
            reqMap.put("send_name",payDto.getSendName());
            reqMap.put("total_num",payDto.getTotalNum());
            reqMap.put("wishing",payDto.getWishing());
            reqMap.put("remark",payDto.getRemark());

            //发放红包使用场景，红包金额大于200或者小于1元时必传
            Integer amount = payDto.getAmount();
            if (amount < 100 || amount > 20000) {
                reqMap.put("scene_id", payDto.getSceneId().getCode());
            }
            String wxUrl = ApolloUtil.getProperty("wx.sendpack.url");

            //构建XML
            String reqStr = WxUtil.directMap2Xml(reqMap, wxSettings.getApiSecret());
            SSLConnectionSocketFactory sslsf = WxUtil.initKeyStoreByPem(wxSettings.getApiCertificate(), wxSettings.getPrivateKey(), wxSettings.getMerchantId());
            CloseableHttpClient httpclient = HttpClients.custom()
                    .setSSLSocketFactory(sslsf)
                    .build();
            HttpPost httpPost = new HttpPost(wxUrl);
            httpPost.setEntity(new StringEntity(reqStr, "UTF-8"));
            CloseableHttpResponse response = httpclient.execute(httpPost);

            String resultStr = "";
            if (response != null && response.getEntity() != null) {
                resultStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                response.close();
            }
            httpclient.close();
            resultMap = WxUtil.doXMLParse(resultStr);
            payBo = new BusinessPayBo();
            if ("SUCCESS".equals(resultMap.get("return_code")) && "SUCCESS".equals(resultMap.get("result_code"))) {
                //调用成功更新状态
                po.setStatus(BusinessPayStatusEnum.CREATE_SUCCESS.getCode());
                businessRecordService.updateRecord(po);

                payBo.setBusinessNo(payDto.getPayNo());
                LOGGER.info("WxCommonAdpter#sendPackage create success pay_no:{},result:{}", payDto.getBusinessNo(), JSON.toJSONString(resultMap));
            } else {
                po.setStatus(BusinessPayStatusEnum.SEND_FAILED.getCode());
                businessRecordService.updateRecord(po);
                LOGGER.error("WxCommonAdpter#sendPackage create failed payDto:{},result:{}", JSON.toJSONString(payDto), JSON.toJSONString(resultMap));
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, resultMap.get("err_code_des"));
            }
        }catch (PaymentException e){
            LOGGER.error("WxCommonAdpter sendPackage error payDto:{}", JSON.toJSON(payDto), e);
            throw e;
        }catch (Exception e){
            LOGGER.error("WxCommonAdpter sendPackage error payDto:{}", JSON.toJSON(payDto), e);
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }
        return payBo;
    }

    public BusinessPayBo bussinessPay(BusinessPayVo payDto) throws PaymentException {
        BusinessPayBo payBo = null;
        SortedMap<String, String> resultMap;

        Map<String, Object> reqMap = new TreeMap<String, Object>(
                new Comparator<String>() {
                    @Override
                    public int compare(String obj1, String obj2) {
                        // 升序排序
                        return obj1.compareTo(obj2);
                    }
                });
        WxSettings wxSettings = channelSettingManager.getWXSettings("", payDto.getBusinessOrderType(), payDto.getPayMode());

        if(Boolean.parseBoolean(ApolloUtil.getProperty("heye_switch_redpack_merchant")) && "saas_pack".equals(payDto.getBusinessOrderType())) {
            WxSettings tempWxSettings = channelSettingManager.getWXSettings("", "yikuaiqian_fm", "mini");
            tempWxSettings.setWxAppId(wxSettings.getWxAppId());
            tempWxSettings.setBusinessOrderType(wxSettings.getBusinessOrderType());
            tempWxSettings.setPayMode(wxSettings.getPayMode());
            wxSettings = tempWxSettings;
        }

        LOGGER.info("WxCommonAdpter#bussinessPay payDto={}, wxSettings={}", JSONObject.toJSONString(payDto), JSONObject.toJSONString(wxSettings));

        //新增记录
        BusinessRecordPo po = VoToPo(payDto,wxSettings);
        payBo = isExit(po);
        if(payBo != null) {
            if (BusinessPayStatusEnum.CREATE.getCode().equals(payBo.getLoad())){
                payDto.setPayNo(payBo.getBusinessNo());
            }else {
                LOGGER.info("WxCommonAdpter bussinessPay end get from db payBo:{}", JSON.toJSONString(payBo));
                return payBo;
            }
        }
        try {
            reqMap.put("mchid", wxSettings.getMerchantId());
            reqMap.put("mch_appid", wxSettings.getWxAppId());
            reqMap.put("nonce_str", WxUtil.getNonceStr());
            reqMap.put("partner_trade_no", payDto.getPayNo());
            //订单总金额，单位为分
            reqMap.put("amount", payDto.getAmount());
            reqMap.put("openid", payDto.getOpenId());
            //NO_CHECK：不校验真实姓名
            //FORCE_CHECK：强校验真实姓名
            if (StringUtils.isEmpty(payDto.getUserName())) {
                reqMap.put("check_name", "NO_CHECK");
            }else {
                reqMap.put("check_name", "FORCE_CHECK");
                reqMap.put("re_user_name", payDto.getUserName());
            }
            reqMap.put("desc",payDto.getRemark());
            String wxUrl = ApolloUtil.getProperty("wx.businesspay.url");

            //构建XML
            String reqStr = WxUtil.directMap2Xml(reqMap, wxSettings.getApiSecret());
            SSLConnectionSocketFactory sslsf = WxUtil.initKeyStoreByPem(wxSettings.getApiCertificate(), wxSettings.getPrivateKey(), wxSettings.getMerchantId());
            CloseableHttpClient httpclient = HttpClients.custom()
                    .setSSLSocketFactory(sslsf)
                    .build();
            HttpPost httpPost = new HttpPost(wxUrl);
            httpPost.setEntity(new StringEntity(reqStr, "UTF-8"));
            CloseableHttpResponse response = httpclient.execute(httpPost);

            String resultStr = "";
            if (response != null && response.getEntity() != null) {
                resultStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                response.close();
            }
            httpclient.close();
            resultMap = WxUtil.doXMLParse(resultStr);
            payBo = new BusinessPayBo();
            if ("SUCCESS".equals(resultMap.get("return_code")) && "SUCCESS".equals(resultMap.get("result_code"))) {
                //调用成功更新状态
                po.setStatus(BusinessPayStatusEnum.CREATE_SUCCESS.getCode());
                businessRecordService.updateRecord(po);

                payBo.setBusinessNo(payDto.getPayNo());
                LOGGER.info("WxCommonAdpter#bussinessPay create success businessNo:{},result:{}", payDto.getBusinessNo(), JSON.toJSONString(resultMap));
            } else {
                po.setStatus(BusinessPayStatusEnum.SEND_FAILED.getCode());
                businessRecordService.updateRecord(po);
                LOGGER.error("WxCommonAdpter#bussinessPay create failed payDto:{},result:{}", JSON.toJSONString(payDto), JSON.toJSONString(resultMap));
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, resultMap.get("err_code_des"));
            }
        }catch (PaymentException e){
            LOGGER.error("WxCommonAdpter bussinessPay error payDto:{}", JSON.toJSON(payDto), e);
            throw e;
        }catch (Exception e){
            LOGGER.error("WxCommonAdpter bussinessPay error payDto:{}", JSON.toJSON(payDto), e);
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }
        return payBo;
    }

    private BusinessRecordPo VoToPo(BusinessPayVo vo,WxSettings settings){
        BusinessRecordPo recordPo = new BusinessRecordPo();
        if (null != vo){
            recordPo.setAmount(vo.getAmount());
            recordPo.setBusinessNo(vo.getBusinessNo());
            recordPo.setBusinessPayType(vo.getBusinessPayType().getCode());
            recordPo.setCallback(vo.getCallback());
            recordPo.setMchId(settings.getMerchantId());
            recordPo.setOpenId(vo.getOpenId());
            recordPo.setPayNo(vo.getPayNo());
            recordPo.setBusinessOrderType(settings.getBusinessOrderType());
            recordPo.setPayMode(settings.getPayMode());
        }
        return recordPo;
    }

    private BusinessPayBo isExit(BusinessRecordPo po){
        BusinessRecordPo exit = businessRecordService.isRepeate(po);
        if (null != exit){
            BusinessPayBo bo = new BusinessPayBo();
            bo.setBusinessNo(exit.getPayNo());
            bo.setLoad(exit.getStatus());
            return bo;
        }else {
            po.setStatus(BusinessPayStatusEnum.CREATE.getCode());
            po.setCreateTime(new Date());
            businessRecordService.addRecord(po);
            return null;
        }
    }

}
